package com.alibaba.user.service.impl;

import com.alibaba.basic.businessexception.BusinessException;
import com.alibaba.basic.service.impl.BaseServiceImpl;
import com.alibaba.basic.util.*;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.system.domain.Menu;
import com.alibaba.system.mapper.MenuMapper;
import com.alibaba.system.mapper.PermissionMapper;
import com.alibaba.user.constant.WxConstants;
import com.alibaba.user.domain.LoginData;
import com.alibaba.user.domain.Logininfo;
import com.alibaba.user.domain.User;
import com.alibaba.user.domain.Wxuser;
import com.alibaba.user.dto.LoginDto;
import com.alibaba.user.mapper.LogininfoMapper;
import com.alibaba.user.mapper.UserMapper;
import com.alibaba.user.mapper.WxuserMapper;
import com.alibaba.user.service.ILogininfoService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 业务实现类：
 */
@Service
public class LogininfoServiceImpl extends BaseServiceImpl<Logininfo> implements ILogininfoService {

    @Autowired
    private LogininfoMapper logininfoMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private WxuserMapper wxuserMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private MenuMapper menuMapper;


    @Override
    public Map<String, Object> accountLogin(LoginDto loginDto) {
        //1.通过account和type去查数据库 - Logininfo对象
        Logininfo logininfo = logininfoMapper.accountLogin(loginDto);
        //2.判断Logininfo对象为null - 账号错误 - 抛异常
        if (logininfo == null) {
            throw new BusinessException("账号错误");
        } else {
            //3.不为null - 判断密码（将md5（密码+盐值） 和  Logininfo对象的密码进行对象）
            String loginPwd = Md5Utils.encrypByMd5(loginDto.getCheckPass() + logininfo.getSalt());
            if (!loginPwd.equals(logininfo.getPassword())) {
                //3.1.密码失败  - 抛异常
                throw new BusinessException("密码错误");
            } else {
                Map<String, Object> map = loginSuccessJwtHandler(logininfo);
                return map;
            }
        }
    }
    private Map<String,Object> loginSuccessJwtHandler(Logininfo logininfo){
        try {
            Map<String, Object> map = new HashMap<>();
            //细节：如果是管理员才需要加四个数据，如果是用户只需要：登录信息对象，jwt串【1,4】
            map.put("logininfo",logininfo);
            // 创建登录信息吧logininfo装进去
            LoginData loginData = new LoginData();
            loginData.setLogininfo(logininfo);

            // 判断登录人的类型0是管理员，1是用户
            if (logininfo.getType()==0){
                //2.当前登录人的所有权限的sn集合 - 昨天写过了 - 直接调用方法
                List<String> permissions = permissionMapper.getPermissionsByLogininfoId(logininfo.getId());
                map.put("permissions",permissions);
                loginData.setPermissions(permissions);

                // 3.当前登录的人的所有权限(显示出来的菜单)
                List<Menu> menus = menuMapper.findMenusByLogininfoId(logininfo.getId());

                map.put("menus",menus);
                loginData.setMenus(menus);

            }
            // 将上面logininfo permissions menus装到logindata中在使用工具类加密
            PrivateKey privateKey = RsaUtils.getPrivateKey(JwtUtils.class.getClassLoader().getResource("auth_rsa.pri").getFile());
            // 暂时没做刷新
            String jwtToken = JwtUtils.generateTokenExpireInMinutes(loginData, privateKey, 30000);
            //二：将四个数据装到map进行返回。疑问：jwt串里面不是已经有登录信息，权限和菜单了吗？ - 前端没有做解密
            map.put("token",jwtToken);
            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    @Override
    public AjaxResult wechatLogin(String code) {
        //     public static final String GET_ACK_URL = "https://api.weixin.qq.com/sns/oauth2/access_token
        //     ?appid=APPID
        //     &secret=SECRET
        //     &code=CODE
        //     &grant_type=authorization_code";
        //1.将WxConstants中的常量替换
        String url = WxConstants.GET_ACK_URL.replace("APPID", WxConstants.APPID)
                .replace("SECRET", WxConstants.SECRET)
                .replace("CODE", code);

        //2.使用httpUtil发送请求
        String jsonStr = HttpUtil.httpGet(url);
        /**
         *  {
         * "access_token":"ACCESS_TOKEN",
         * "expires_in":7200,
         * "refresh_token":"REFRESH_TOKEN",
         * "openid":"OPENID",
         * "scope":"SCOPE",
         * "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
         * }
         */
         // 3.对返回的json数据做处理
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        String accessToken = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");

        // 4.根据openid去查
        Wxuser wxUser = wxuserMapper.findByOpenid(openid);

        // wxUser存在，登陆过
        if (wxUser !=null && wxUser.getUserId() != null){
            // 通过userID查询user
            User user = userMapper.findById(wxUser.getUserId());

            // user可能为null
            if (user != null){
                // 通过user找到logininfo
                Logininfo logininfo = logininfoMapper.findById(user.getLogininfoId());
                Map<String, Object> map = loginSuccessJwtHandler(logininfo);
                return AjaxResult.me().setResultObj(map);

            }
        }
        //4.2.如果没有= 没有绑定 = return false = 让前端去跳转到绑定页面
        // ?accessToken=xxxx&openId=yyy;
        String params = "?accessToken="+accessToken+"&openId="+openid;
        return AjaxResult.me().setSuccess(false).setResultObj(params);

    }

    @Override
    public AjaxResult wechatBinder(Map<String, String> map) {
        // 获取四个参数： phone:"13330964748",
        //				verifyCode:"",
        //				accessToken:"",
        //				openId:""
        String phone = map.get("phone");
        String verifyCode = map.get("verifyCode");
        String accessToken = map.get("accessToken");
        String openId = map.get("openId");

        // 1.空值判断
        if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(verifyCode)){
            throw new BusinessException("手机或者验证码为空");
        }
        // 校验手机验证码是否过期
        Object code = redisTemplate.opsForValue().get("binder:" + phone);
        // 2.验证码不存在了过期了
        if (code == null){
            throw new BusinessException("手机验证码过期，请重新发送");
        }

        // 没过期，对比是否相等
        if (!code.toString().split(":")[0].equals(verifyCode)){
            throw new BusinessException("手机验证码错误,请重新输入");
        }
        // 3.第三个请求更换参数
        String url = WxConstants.GET_USER_URL
                .replace("OPENID", openId)
                .replace("ACCESS_TOKEN", accessToken);
        String jsonStr = HttpUtil.httpGet(url);
        // 处理json数据 将json转换成wxuser对象
        Wxuser wxuser = jsonStr2Wxuser(jsonStr);

        // 4.绑定流程
        // 4.1通过电话号码去查询user
        User user = userMapper.findByPhone(phone);

        // 判断user是否存在
        if (user == null){
            // 不存在，直接把手机号保存为对象
            user = phone2User(phone);
            // 再将user对象转换成logininfo对象
            Logininfo logininfo = user2logininfo(user);
            // 直接保存 id自增
            logininfoMapper.add(logininfo);
            // 吧logininfoId传给user，在保存
            user.setLogininfoId(logininfo.getId());
            userMapper.add(user);
        }

        // user存在的情况,关联user和wxuser表，通过userid
        wxuser.setUserId(user.getId());
        wxuserMapper.add(wxuser);

        // 4.2免密登录,免密和盐值不重要，主要是授权,返回token和loginin
        // 通过user对象的logininfoid查询
        Logininfo logininfo = logininfoMapper.findById(user.getLogininfoId());
        Map<String, Object> result = loginSuccessJwtHandler(logininfo);

        return AjaxResult.me().setResultObj(result);
    }

    // 对象转移方法
    private Logininfo user2logininfo(User user) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user,logininfo);
        logininfo.setType(1);
        logininfo.setDisable(true);
        return logininfo;
    }

    private User phone2User(String phone) {
        User user = new User();
        user.setUsername(phone);
        user.setPhone(phone);
        String salt = StrUtils.getComplexRandomString(32);
        String pwd = StrUtils.getComplexRandomString(10);
        String md5Pwd = Md5Utils.encrypByMd5(pwd + salt);
        user.setSalt(salt);
        user.setPassword(md5Pwd);
        return user;
    }
    /*
{
    "openid":"OPENID",
    "nickname":"NICKNAME",
    "sex":1,
    "province":"PROVINCE",
    "city":"CITY",
    "country":"COUNTRY",
    "headimgurl": "https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
    "privilege":[
    "PRIVILEGE1",
    "PRIVILEGE2"
    ],
    "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
    }
 */
    private Wxuser jsonStr2Wxuser(String jsonStr) {
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        Wxuser wxuser = new Wxuser();
        wxuser.setOpenid(jsonObject.getString("openid"));
        wxuser.setNickname(jsonObject.getString("nickname"));
        wxuser.setSex(jsonObject.getInteger("sex"));
        wxuser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxuser.setUnionid(jsonObject.getString("unionid"));
        wxuser.setOpenid(jsonObject.getString("openid"));
        String address = jsonObject.getString("country")+
                jsonObject.getString("province")+
                jsonObject.getString("city");
        wxuser.setAddress(address);
        return wxuser;

    }


}
